package my.suveng.util.aspect;

import cn.hutool.core.lang.caller.CallerUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.log.Log;
import cn.hutool.log.LogFactory;
import com.google.common.base.Throwables;
import my.suveng.model.common.exceptions.AbstractSystemException;
import my.suveng.model.common.interfaces.define.SpringUtil;
import my.suveng.util.json.Jackson;
import my.suveng.util.log.LogDetail;
import my.suveng.util.log.PlusLogFactoryHutool;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.util.Objects;

/**
 * description:
 * @author suwenguang
 * @date 2019-08-14
 * @version 1.0.0
 **/
@SpringUtil
@Aspect
@Component
public class LogAspect {

    //hutool日志
    private static final Log log = LogFactory.setCurrentLogFactory(new PlusLogFactoryHutool()).getLog(CallerUtil.getCaller().getName());

    /**
     * 任意方法切面表达式
     * @author suwenguang
     * @date 2019-09-25
     */
    @Pointcut(value = "execution(* *..*.*.*(..)) ")
    public void anyMethod() {
    }

    /**
     * 切面方法,用于接口切面AOP增强
     * @author suwenguang
     * @date 2019-09-25
     */
    @Around(value = "anyMethod() && @annotation(requestMapping) && @annotation(apiLog) ", argNames = "pjp,requestMapping,apiLog")
    public Object aroundMethod(ProceedingJoinPoint pjp, RequestMapping requestMapping, ApiLog apiLog) {

        LogDetail logDetail = LogDetail.get();
        // 设置tag
        HttpServletRequest request = ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();
        logDetail.setTag(request.getRequestURI());
        logDetail.setRequsetId(IdUtil.fastSimpleUUID());

        Object[] args = pjp.getArgs();
        log.info("请求参数=" + Jackson.toJsonString(args));

        long start = System.nanoTime();
        try {
            //执行
            Object res = pjp.proceed();
            //响应时间
            long end = System.nanoTime();
            long cost = (end - start) / 1000000;

            //返回结果
            log.info("返回结果={},响应时间={}ms", res.toString(), cost);

            return res;
        } catch (Throwable throwable) {
            if (!(throwable instanceof AbstractSystemException)) {
                log.error("方法{}出现异常,{}", pjp.getSignature().getDeclaringTypeName(), throwable);
            }
            throw Throwables.propagate(throwable);
        } finally {
            resetLogDetail();
        }
    }


    /**
     * 切面方法,用于接口切面AOP增强
     * @author suwenguang
     * @date 2019-09-25
     */
    @Around(value = "anyMethod() && @annotation(postMapping) && @annotation(apiLog) ", argNames = "pjp,postMapping,apiLog")
    public Object aroundMethod(ProceedingJoinPoint pjp, PostMapping postMapping, ApiLog apiLog) {
        LogDetail logDetail = LogDetail.get();
        // 设置tag
        HttpServletRequest request = ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();
        logDetail.setTag(request.getRequestURI());
        logDetail.setRequsetId(IdUtil.fastSimpleUUID());

        Object[] args = pjp.getArgs();
        log.info("请求参数=" + Jackson.toJsonString(args));

        long start = System.nanoTime();
        try {
            //执行
            Object res = pjp.proceed();
            //响应时间
            long end = System.nanoTime();
            long cost = (end - start) / 1000000;

            //返回结果
            log.info("返回结果={},响应时间={}ms", res.toString(), cost);

            return res;
        } catch (Throwable throwable) {
            if (!(throwable instanceof AbstractSystemException)) {
                log.error("方法{}出现异常,{}", pjp.getSignature().getDeclaringTypeName(), throwable);
            }
            throw Throwables.propagate(throwable);
        } finally {
            resetLogDetail();
        }
    }

    /**
     * 重置线程的logdetail
     * @author suwenguang
     */
    private void resetLogDetail() {
        LogDetail logDetail = LogDetail.get();
        logDetail.setTag("");
        logDetail.setRequsetId("");
    }
}
